const Joi = require("joi");
const AliPayUtil = require("../../utils/pay/alipay");
const WechatPayUtil = require("../../utils/pay/wechat");
const moment = require("moment");
module.exports = class PayController extends colpu.Controller {
  constructor(ctx) {
    super(ctx);
    const {
      config: { alipay, wxpay },
    } = ctx;
    // 支付宝
    this.alipay = new AliPayUtil(
      {
        // gateway: "https://openapi.alipay.com/gateway.do",
        appId: alipay.appId,
        privateKey: alipay.privateKey,
        // alipayPublicKey: alipay.publicKey,
        // 支付宝根证书路径，下载后保存位置的绝对路径
        appCertPath: alipay.certPublicKey,
        alipayRootCertPath: alipay.rootCert,
        // 支付宝公钥证书路径，下载后保存位置的绝对路径
        alipayPublicCertPath: alipay.certPublicKeyRSA2,
      },
      ctx
    );
    // 微信支付
    this.wechat = new WechatPayUtil(
      {
        appid: wxpay.appId, // 直连商户申请的公众号或移动应用appid
        mchid: wxpay.mchid, // 商户号
        // serial_no: wxpay.serialNo, // 证书序列号（可选参数）
        publicKey: wxpay.publicKey, // 公钥
        privateKey: wxpay.privateKey, // 秘钥
        // authType: "SHA256",
        key: wxpay.apiv3,
      },
      ctx
    );
  }

  /**
   * @api {post} /api/vi/apppay App支付交易
   * @apiDescription App支付交易
   * @apiGroup  支付接口
   * @apiVersion 1.0.0
   *
   * @apiParam {String} [uid] 用户唯一uid
   * @apiParam {Number} [pro_id] 产品id
   * @apiParam {Number} [pay_type] 支付产品类型，对应享有权限处理
   * @apiParam {String} [amount] 支付金额
   * @apiParam {String} [subject] 支付标题
   * @apiParam {String} [desc] 支付描述
   *
   * @apiSuccess {Number} status 状态码
   * @apiSuccess {String} message 消息
   * @apiSuccess {Object} data 返回结果
   * @apiSuccess {Boolen} isDecrypt 是否需要解密
   * @apiSuccessExample {json} Success-Response:
   * HTTP/1.1 200 OK
      {
        "data": {
          ...
        },
        "status": 0,
        "message": "ok"
      }
   *
   */
  async tradeAppPay(ctx) {
    await ctx.validateAsync({
      body: {
        uid: Joi.string().required(),
        pro_id: Joi.number().required(),
        pay_type:
          ctx.query.version == "1.0.0" ? Joi.number() : Joi.number().required(),
        amount: Joi.number().required(),
        subject: Joi.string().required(),
        desc: Joi.string(),
        type: Joi.number().required(), // 支付类型
      },
      status: 10001,
    });
    const outTradeNo = this.wechat.uuid();
    let orderInfo;
    const body = ctx.request.body;
    const attachData = this._attachData(body);
    // 修改价格，便于测试支付。
    if (ctx.app.env === "development") {
      body.amount = 1;
    }
    switch (body.type) {
      case 2:
        orderInfo = this.alipay.order(body, outTradeNo, attachData);
        break;
      case 1:
        orderInfo = await this.wechat.order(body, outTradeNo, attachData);
        break;
      default:
        return ctx.throw(-1, "类型不对");
    }
    // 这里需要记录支付订单结果，便于后面做处理支付完成做状态修改
    await this._savePayLog(body, outTradeNo);
    ctx.respond(orderInfo);
  }

  _savePayLog(body, outTradeNo) {
    const data = {
      pro_id: body.pro_id,
      pro_name: body.subject,
      uid: body.uid,
      order_id: outTradeNo,
      pay_type: body.pay_type,
      price: body.price,
      discount: body.amount,
      status: body.status || 0,
      end_time: body.end_time,
      create_time: moment().format("YYYY-MM-DD hh:mm:ss"),
    };
    console.log(data);
    return this.service.pay.savePayLog(data);
  }
  // 附加数据微信只支持128个字符 attach	string[1,128]
  _attachData(data) {
    return {
      uid: data.uid, // 用户唯一uid
      pro_id: data.pro_id, // 产品id
      pay_type: data.pay_type, // 产品类型，比如vip1月
    };
  }

  alipayCallback(ctx) {
    const body = ctx.request.body;
    // {
    //   gmt_create: '2023-08-07 14:59:15',
    //   charset: 'utf-8',
    //   seller_email: 'ycg520520@126.com',
    //   subject: '一个馒头',
    //   sign: 'iM7TyVub0L0s92b4EFJcgEURYPGqXHsZpMgEXVENT2J+SWqYogzOEwx5K8H+AuljoVC/I7wObz+Ys4JmSb6Foz2erPh1DQtRk3t61eh+ZZv8prjW3U5tm0TpaCat50oLWy7X2PDNDMyRyoj/MUcirxwZ6dXUn0ei7XBs0J/pL4meBNz6ONN7iq791MKqhXDxr7KwmmpMHIJ2G4d8AsJycv4LfoshyogxXLQq72cbdojUeTtF/3fdbZOy+G+uKsskz0n+ByQNCi7tIh1CMxUzu9MidvP4pll5rFezfoR4Hkgc6OqCm7SXEHh/66WDZGKS/cUqLbgjNR6et/07aGNyhA==',
    //   body: '尽享一月无广告打扰，心愿值+2加速！',
    //   buyer_id: '2088002069895649',
    //   invoice_amount: '0.01',
    //   notify_id: '2023080701222145916095641402343965',
    //   fund_bill_list: '[{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}]',
    //   notify_type: 'trade_status_sync',
    //   trade_status: 'TRADE_SUCCESS',
    //   receipt_amount: '0.01',
    //   app_id: '2021003189648028',
    //   buyer_pay_amount: '0.01',
    //   sign_type: 'RSA2',
    //   seller_id: '2088641085207030',
    //   gmt_payment: '2023-08-07 14:59:15',
    //   notify_time: '2023-08-07 14:59:16',
    //   version: '1.0',
    //   out_trade_no: 'a8a6d16ea45446c89ed3245381532160',
    //   total_amount: '0.01',
    //   trade_no: '2023080722001495641453207149',
    //   auth_app_id: '2021003189648028',
    //   buyer_logon_id: 'ycg***@qq.com',
    //   point_amount: '0.00'
    // }
    /* 实际验证过程建议商户添加以下校验。
    1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
    2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
    3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
    4、验证app_id是否为该商户本身。
    */
    console.log("alipay支付回调开始");
    const flag = this.alipay.checkNotifySign(body);
    console.log("alipay支付回调", flag);
    if (flag) {
      const { out_trade_no, passback_params } = body;
      const { uid, pro_id, pay_type } = JSON.parse(passback_params);
      // 并做数据支付更新
      this._updatePayInfo({ order_id: out_trade_no, uid, pro_id, pay_type });
    }
    // 返回success & fail，是alipay 所需的，这样可以及时更新数据避免多次回调
    console.log("alipay支付回调结束");
    ctx.body = flag ? "success" : "fail";
  }
  async wechatCallback(ctx) {
    const body = ctx.request.body;
    const { headers } = ctx;
    /**
     * 验证签名，提醒：node 取头部信息时需要用小写，例如：req.headers['wechatpay-timestamp']
     * @param params.timestamp HTTP头Wechatpay-Timestamp 中的应答时间戳
     * @param params.nonce HTTP头Wechatpay-Nonce 中的应答随机串
     * @param params.body 应答主体（response Body），需要按照接口返回的顺序进行验签，错误的顺序将导致验签失败。
     * @param params.serial HTTP头Wechatpay-Serial 证书序列号
     * @param params.signature HTTP头Wechatpay-Signature 签名
     * @param params.apiSecret APIv3密钥，如果在 构造器 中有初始化该值(this.key)，则可以不传入。当然传入也可以
     */
    console.log("微信支付回调开始");
    const flag = await this.wechat.verifySign({
      timestamp: headers["wechatpay-timestamp"],
      nonce: headers["wechatpay-nonce"],
      body: body,
      serial: headers["wechatpay-serial"],
      signature: headers["wechatpay-signature"],
    });
    console.log("微信支付回调", flag);
    if (flag) {
      const { ciphertext, associated_data, nonce } = body.resource;
      // 解密订单信息
      const result = this.wechat.decipher_gcm(
        ciphertext,
        associated_data,
        nonce
      );
      // payer: { openid: 'oYfY26Gz_K0-CJ5GZ08TNbc8fmqc' },
      // attach: '{"uid":"user.data.uid","pro_id":1,"pay_type":4}',
      // out_trade_no: 'df17c65a5bde41ceb65bef25bf79e63d',
      console.log("微信支付回调:", result);
      const { attach, out_trade_no } = result;
      const { uid, pro_id, pay_type } = JSON.parse(attach);
      this._updatePayInfo({ order_id: out_trade_no, uid, pro_id, pay_type });
    }
    console.log("微信支付回调结束");
    ctx.body = flag ? "success" : "fail";
  }

  async _updatePayInfo(params) {
    console.log("更新用户支付信息");
    // 获得支付类型数据
    const payTypes = await this._getPayTypes();
    // 更新支付日志，确定是否已经付费
    await this._updatePaySuccessInfo(params, payTypes);

    // 更新用户VIP时间，根据当前pay_type类型决定VIP过期时间
    this._updateVIPUserExprise(params.uid, payTypes);

    // 如果用户充个值的单个产品，而非VIP，则根据pay_type去记录充值的产品pid
    // 以后这里扩展功能
  }

  // 更新支付成功后信息，需要更新状态，和支付时间和结束时间
  async _updatePaySuccessInfo(params, payTypes) {
    const { order_id, uid } = params;
    // 这里查出用户VIP到期时间，作为续期时间的开始，如果没有，则以当前时间开始
    let vipStartTime = moment().format("YYYY-MM-DD hh:mm:ss");
    const currentType = payTypes[params.pay_type];
    // VIP才做时间累计
    if (currentType.fid === 11 || currentType.id === 11) {
      const data = await this.service.pay.getLastPayLog(uid);
      if (data && data.end_time) {
        vipStartTime = moment(data.end_time).format("YYYY-MM-DD hh:mm:ss");
      }
    }
    const endTime = this._setVIPEndTime(
      vipStartTime,
      payTypes,
      params.pay_type.toString()
    );
    console.log("endTime:", endTime);
    this.service.pay.updatePayLog({
      order_id,
      status: 1,
      start_time: vipStartTime,
      end_time: endTime,
    });
  }

  async _getPayTypes() {
    return this.service.productPayType.payType({
      mode: "object",
    });
  }
  // 设置VIP结束时间，通过开始时间和payType决定时间
  _setVIPEndTime(startTime, payTypes, key) {
    const power = payTypes[key].power.split(",");
    let endTime = moment(startTime)
      .add(power[0], power[1])
      .format("YYYY-MM-DD hh:mm:ss");
    return endTime;
  }

  // 更新用户VIP时间，根据当前pay_type类型决定VIP过期时间
  // 年:y,季度:Q;几个月:M;周:w;天:d;小时:h;分钟:m;秒:s;毫秒:ms
  async _updateVIPUserExprise(uid, payTypes) {
    // 获取到用户充值权益未失效的数据
    const data = await this.service.pay.getPayLog(uid);
    // 没获取到用户的充值记录则，返回
    if (!data.length) return;

    // 获得VIP最早开始时间
    const updateVipExpire = moment(data[0].start_time);
    data.forEach((item, i) => {
      // 由于健值是字符串，所以这里需要转换一下
      const key = item.pay_type.toString();
      if (Object.keys(payTypes).indexOf(key) == -1) {
        return;
      }
      // 以毫秒为单位的时间间隔
      const timeStep = moment(item.end_time) - moment(item.start_time);
      // 将时间间隔加到最早开始时间上，从而获得从最早时间到最后充值时间VIP时长
      updateVipExpire.add(timeStep, "ms");
    });

    this.service.user.updateExprise({
      uid,
      vip_expire: updateVipExpire.format("YYYY-MM-DD hh:mm:ss"),
    });
  }

  async shareWelfare(ctx) {
    await ctx.validateAsync({
      body: {
        uid: Joi.string().required(),
        pro_id: Joi.number().required(),
        pay_type: Joi.number().required(),
        amount: Joi.number().required(),
        subject: Joi.string().required(),
        desc: Joi.string(),
      },
      status: 10001,
    });

    const body = ctx.request.body;
    const { count, latest } = await this.service.pay.getShareWelfare({
      uid: body.uid,
      pay_type: body.pay_type,
    });

    // 获得支付类型数据
    const payType = body.pay_type;
    const payTypes = await this._getPayTypes();
    const { plus = 1 } = payTypes[payType];

    // 如果分享次数大于3次将不在获得分享VIP权限
    const MAX_SHARE = 10;
    if (latest) {
      const latestEndTime = latest.end_time;
      if (count >= MAX_SHARE) {
        return ctx.respond({
          count: MAX_SHARE,
          plus,
          vip_expire: latestEndTime,
          message: `恭喜，您已最大获得分享${MAX_SHARE}次VIP权限～`,
        });
      }
      // 如果还有分享权限，这时已经存在分享VIP则直接返回
      if (moment(latestEndTime) > moment()) {
        return ctx.respond({
          count,
          plus,
          vip_expire: latestEndTime,
          message: `恭喜，您当前已获得分享${count}次VIP权限哦～`,
        });
      }
    }

    let vipStartTime = moment().format("YYYY-MM-DD hh:mm:ss");
    const endTime = this._setVIPEndTime(vipStartTime, payTypes, payType);
    const outTradeNo = this.wechat.uuid();
    body.type = 0;
    body.status = 1;
    body.start_time = vipStartTime;
    body.end_time = endTime;
    await this._savePayLog(body, outTradeNo);
    // 更新用户过期日期
    await this._updateVIPUserExprise(body.uid, payTypes);
    const latestCount = count + 1;
    ctx.respond({
      count: latestCount,
      plus,
      vip_expire: endTime,
      message: `恭喜，您已获得分享${latestCount}次VIP权限哦～`,
    });
  }
};
